package redis

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/gomodule/redigo/redis"
	"github.com/kataras/iris/v12"
	"github.com/qf-tech/qftech-base/pkg/log"
)

// RedisEngine def
type RedisEngine struct {
	pool *redis.Pool
}

// RedisClient export var
var RedisClient *RedisEngine

type Config struct {
	Password string
	Host     string
	Port     int
}

// NewEngine def
func NewEngine(config Config) *RedisEngine {
	pool := &redis.Pool{
		//连接方法
		Dial: func() (redis.Conn, error) {
			redisAddr := fmt.Sprintf("%s:%d", config.Host, config.Port)
			c, err := redis.Dial("tcp", redisAddr, redis.DialPassword(config.Password))
			if err != nil {
				return nil, err
			}
			_, _ = c.Do("SELECT", 0)
			return c, nil
		},
		//DialContext:     nil,
		//TestOnBorrow:    nil,
		//最大的空闲连接数，表示即使没有redis连接时依然可以保持N个空闲的连接，而不被清除，随时处于待命状态。
		MaxIdle: 1,
		//最大的激活连接数，表示同时最多有N个连接
		MaxActive: 10,
		//最大的空闲连接等待时间，超过此时间后，空闲连接将被关闭
		IdleTimeout: 180 * time.Second,
		//Wait:            false,
		//MaxConnLifetime: 0,
	}

	redisEngine := &RedisEngine{
		pool: pool,
	}
	return redisEngine
}

// InitRedisPool def
func InitRedisPool(config Config) error {
	RedisClient = NewEngine(config)
	return nil
}

func redisArgs(key string, args ...interface{}) redis.Args {
	redisArgs := redis.Args{}
	redisArgs = redisArgs.Add(key)
	for _, a := range args {
		redisArgs = redisArgs.AddFlat(a)
	}

	return redisArgs
}

// cmdNoCtx def
func cmdNoCtx(cmd string, args ...interface{}) (interface{}, error) {
	client := RedisClient.pool.Get()
	defer client.Close()
	result, err := client.Do(cmd, args...)
	if err != nil {
		return nil, err
	}

	return result, nil
}

// CmdNoCtx def
func CmdNoCtx(ctx iris.Context, cmd string, key string, args ...interface{}) (interface{}, error) {
	log := log.GetLogger().GetCtx(ctx.Request().Context()).Sugar()
	redisArgs := redisArgs(key, args...)
	result, err := cmdNoCtx(cmd, redisArgs...)
	if err != nil {
		time.Sleep(10 * time.Millisecond)
		result, e := cmdNoCtx(cmd, redisArgs...)
		if e != nil {
			log.Warnf("cmd: %s, key: %s, args:%v, err:%v", cmd, key, args, e)
			return nil, e
		}

		// 避免出现打印超大日志，做了key命令的取舍
		if strings.ToUpper(cmd) == "KEYS" {
			log.Infof("opt redis succeed, cmd: %s, key: %s, args:%v", cmd, key, args)
		} else {
			log.Infof("opt redis succeed, cmd: %s, key: %s, args:%v, result: %v", cmd, key, args, result)
		}
		return result, nil
	}

	// 避免出现打印超大日志，做了key命令的取舍
	if strings.ToUpper(cmd) == "KEYS" {
		log.Infof("opt redis succeed, cmd: %s, key: %s, args:%v", cmd, key, args)
	} else {
		log.Infof("opt redis succeed, cmd: %s, key: %s, args:%v, result: %v", cmd, key, args, result)
	}
	return result, nil
}

// GetDigestInfo def
func GetDigestInfo(c iris.Context, digest string) (string, error) {
	value, err := redis.String(CmdNoCtx(c, "GET", digest))
	if err != nil {
		return "", err
	}

	return value, nil
}

// SetDigestInfo def
func SetDigestInfo(c iris.Context, digest string, value string, expire int) error {
	var args []string
	args = append(args, value)
	_, err := CmdNoCtx(c, "SET", digest, args)
	if err != nil {
		return err
	}

	var args1 []int
	args1 = append(args1, expire)
	_, err = CmdNoCtx(c, "EXPIRE", digest, args1)
	if err != nil {
		return err
	}

	return nil
}

// GetTTL def
func GetTTL(c iris.Context, key string) (int64, error) {
	value, err := redis.Int64(CmdNoCtx(c, "ttl", key))
	if err != nil {
		return 0, err
	}

	return value, nil
}

// MembersIPInfo def
func MembersIPInfo(c iris.Context, key string) ([]string, error) {
	members, err := redis.Strings(CmdNoCtx(c, "SMEMBERS", key))
	if err != nil {
		return nil, err
	}

	return members, nil
}

// Lock def
func Lock(ctx iris.Context, mutex *sync.Mutex, key string, value string, second int) (bool, error) {
	mutex.Lock()
	defer mutex.Unlock()

	log := log.GetLogger().GetCtx(ctx.Request().Context()).Sugar()
	redisArgs := redisArgs(key, value, "EX", second, "NX")
	res, err := cmdNoCtx("SET", redisArgs...)
	result, err := redis.String(res, err)
	if err != nil {
		log.Errorf("cmd:SET, key: %s, args:%v, err:%v", key, redisArgs, err)
		return false, err
	}

	log.Infof("cmd:SET, key: %s, args:%v succeed", key, redisArgs)

	if result == "" {
		return false, nil
	}
	return true, nil
}

// UnLock def
func UnLock(ctx iris.Context, mutex *sync.Mutex, key string) error {
	mutex.Lock()
	defer mutex.Unlock()

	log := log.GetLogger().GetCtx(ctx.Request().Context()).Sugar()
	redisArgs := redisArgs(key)
	_, err := cmdNoCtx("DEL", redisArgs...)
	if err != nil {
		log.Errorf("cmd:SET, key: %s, args:%v, err:%v", key, redisArgs, err)
		return err
	}

	log.Infof("cmd:DEL, key: %s, args:%v succeed", key, redisArgs)

	return nil
}
